#include "CG_toggle_vector.h"


using namespace std;


const int DELTA_LETTER_TO_NUMBER = 49;


long togglle_vector_get_num_elements(const char*);


CG_toggle_vector::CG_toggle_vector()
{
	this->data = NULL;
	this->org_data = NULL;
	this->length = 0;
	this->num_toggles = -1;
	this->num_elements =0;
}


CG_toggle_vector::CG_toggle_vector(const CG_toggle_vector& aVector)
{
	if(0 < this->length)
	{
		delete[] this->org_data;
		delete[] this->org_data; 
	}
	this->org_data = NULL;
	this->data = NULL;
	this->num_elements = 0;

	if(NULL != aVector.org_data)
	{
		try
		{
			this->org_data = new char[strlen(aVector.org_data) + 1];
		}
		catch(std::bad_alloc e)
		{
			cerr << "CG_toggle_vector (1) : Bad memory allocation" << endl;
			return;
		}
		memset(this->org_data, '\0', strlen(aVector.org_data) + 1);
		strcpy(this->org_data, aVector.org_data);
	}
	this->length = aVector.length;
	if(this->length <= 0 || aVector.num_elements <= 0)
	{
		cerr << "CG_toggle_vector : Bad input" << endl;
		delete[] this->org_data;
		this->org_data = NULL;
		this->num_toggles = -1;
		return;
	}

	this->num_elements = aVector.num_elements;
	try
	{
		this->data = new long[this->num_elements];
	}
	catch(std::bad_alloc e)
	{
		cerr << "CG_toggle_vector (2) : Bad memory allocation" << endl;
		delete[] this->org_data;
		this->org_data = NULL;
		this->num_toggles = -1;
		this->data = NULL;
		return;
	}
	for(int idx = 0; idx < this->num_elements; idx++)
	{
		this->data[idx] = aVector.data[idx];
	}
	this->num_toggles = aVector.num_toggles;
}
		

CG_toggle_vector::CG_toggle_vector(const char *aData)
{
	this->org_data = NULL;
	this->data = NULL;
	this->num_elements = 0;
	this->length = 0;

	if(NULL == aData)
	{
		return;
	}
	try
	{
		this->org_data = new char[strlen(aData) + 1];
	}
	catch(std::bad_alloc e)
	{
		cerr << "CG_toggle_vector (1) : Bad memory allocation" << endl;
		return;
	}
	memset(this->org_data, '\0', strlen(aData) + 1);
	strcpy(this->org_data, aData);

	if(false == this->parse_elements(aData))
	{
		cerr<<"Failed parse elements"<<endl;
		delete[] this->org_data;
		this->org_data = NULL;
		return;
	}
	
	for(long idx = 0; idx < this->num_elements; idx++)
	{
		this->length += this->data[idx];
	}
}


CG_toggle_vector::~CG_toggle_vector(void)
{
	if(NULL != this->data)
	{
		delete[] this->data;
	}
	if(NULL != this->org_data)
	{
		delete[] this->org_data; 
	}
}


CG_toggle_vector& CG_toggle_vector::operator=(const CG_toggle_vector& new_vector)
{
	if(this == &new_vector)
	{
		return *this;
	}
	if(NULL != this->org_data)
	{
		delete[] this->org_data;
	}
	if(NULL != this->data)
	{
		delete[] this->data;
	}
	this->org_data = NULL;
	this->data = NULL;
	this->num_elements = 0;
	this->length = 0;

	if(NULL != new_vector.org_data)
	{
		try
		{
			this->org_data = new char[strlen(new_vector.org_data) + 1];
		}
		catch(std::bad_alloc e)
		{
			cerr << "CG_toggle_vector operator= : Bad memory allocation" << endl;
			return *this;
		}
		memset(this->org_data, '\0', strlen(new_vector.org_data) + 1);
		strcpy(this->org_data, new_vector.org_data);
	}
	this->length = new_vector.length;
	if(this->length <= 0 || new_vector.num_elements <= 0)
	{
		cerr << "CG_toggle_vector : Bad input" << endl;
		delete[] this->org_data;
		this->org_data = NULL;
		this->num_toggles = -1;
		return *this;
	}

	this->num_elements = new_vector.num_elements;
	try
	{
		this->data = new long[this->num_elements];
	}
	catch(std::bad_alloc e)
	{
		cerr << "CG_toggle_vector oipewrator= (2) : Bad memory allocation" << endl;
		delete[] this->org_data;
		this->org_data = NULL;
		this->num_toggles = -1;
		this->data = NULL;
		return *this;
	}
	for(int idx = 0; idx < this->num_elements; idx++)
	{
		this->data[idx] = new_vector.data[idx];
	}
	this->num_toggles = new_vector.num_toggles;

	return *this;
}


bool CG_toggle_vector::CG_toggle_vector_set_data(const char *aData)
{
	if(NULL == aData)
	{
		return false;
	}

	if(NULL != this->data)
	{
		delete[] this->data;
		this->data = NULL;
		this->length = 0;
		this->num_elements = 0;
	}
	if(NULL != this->org_data)
	{
		delete[] this->org_data; 
		this->org_data = NULL;
	}

	try
	{
		this->org_data = new char[strlen(aData) + 1];
	}
	catch(std::bad_alloc e)
	{
		cerr << "CG_toggle_vector (1) : Bad memory allocation" << endl;
		this->org_data = NULL;
		return false;
	}
	memset(this->org_data, '\0', strlen(aData) + 1);
	strcpy(this->org_data, aData);

	if(false == this->parse_elements(aData))
	{
		cerr<<"Failed parse elements"<<endl;
		delete[] this->org_data;
		this->org_data = NULL;
		return false;
	}
	
	for(long idx = 0; idx < this->num_elements; idx++)
	{
		this->length += this->data[idx];
	}

	return true;
}


char *CG_toggle_vector::CG_toggle_vector_get_org_data() const 
{
	return this->org_data;
}


long CG_toggle_vector::get_next_toggle(long index)
{
	long curr_element;
	long curr_index;
	TOGGLE_BIN curr_result;

	if(index < 0 || index > this->length)
	{
		return -1;
	}

	for(curr_element = 0, curr_index = 0, curr_result = ZERO; curr_element < this->num_elements; curr_element++)
	{
		if(ONE == curr_result && index < curr_index + this->data[curr_element])
		{
			if(index >= curr_index)
			{
				return index + 1;
			}
			else
			{
				return curr_index + 1;
			}
		}
		curr_index += this->data[curr_element];
		curr_result = (curr_result == ZERO) ? ONE : ZERO;
	}

	return -1;
}
		

TOGGLE_BIN CG_toggle_vector::operator[](long idx)
{
	long curr_element;
	long curr_index;
	TOGGLE_BIN curr_result;

	if(idx < 0 || idx > this->length)
	{
		return ZERO;
	}

	for(curr_element = 0, curr_index = 0, curr_result = ZERO; curr_element < this->num_elements; curr_element++)
	{
		if(idx < this->data[curr_element] + curr_index)
		{
			return curr_result;
		}
		curr_index += this->data[curr_element];
		curr_result = (curr_result == ZERO) ? ONE : ZERO;
	}

	return ZERO;
}
		

long CG_toggle_vector::get_length()
{
	return this->length;
}
		

long CG_toggle_vector::get_num_toggles()
{
	long curr_element;
	long prev_index;
	TOGGLE_BIN curr_result;

	if(-1 != this->num_toggles)
	{
		return this->num_toggles;
	}

	this->num_toggles = 0;
	for(curr_element = 0, prev_index = 0, curr_result = ZERO; curr_element < this->num_elements; curr_element++)
	{
		if(ONE == curr_result)
		{
			this->num_toggles += (this->data[curr_element] - prev_index);
			curr_result = (curr_result == ZERO) ? ONE : ZERO;
		}
		prev_index += this->data[curr_element];
	}

	return this->num_toggles;
}


long CG_toggle_vector::get_num_elements()
{
	return this->num_elements;
}


bool CG_toggle_vector::parse_elements(const char *data)
{
	TOGGLE_BIN curr_element_status = ZERO;
	long data_length;
	long curr_element = 0;

	if(this->length > 0)
	{
		return false;
	}

	this->num_elements = togglle_vector_get_num_elements(data);
	if(this->num_elements <= 0)
	{
		return false;
	}

	try
	{
		this->data = new long[this->num_elements];
	}
	catch(exception e)
	{
		cerr << "parse_elements (1): " << e.what() << endl;
		this->data = NULL;
		this->num_elements = 0;
		return false;
	}
	
	data_length = strlen(data);
	for(long idx = 0; idx < data_length; idx++)
	{
		if(ZERO == curr_element_status)
		{
			long start = idx;
			long end = idx;
			char *temp_str;
			long internal_idx;
			for(internal_idx = start; internal_idx < data_length; internal_idx++)
			{
				if(data[internal_idx] >= 'a' && data[internal_idx] <= 'j')
				{
					curr_element_status = ONE;
					end = internal_idx - 1;
					idx = end;
					break;
				}
			}
			if(internal_idx == data_length)
			{
				end = internal_idx - 1;
				idx = end;
			}
			if(start > end)
			{
				this->data[curr_element] = 0;
				curr_element++;
			}
			else
			{
				long temp_str_index;

				try
				{
					temp_str = new char[(end + 2) - start];
				}
				catch(std::bad_alloc e)
				{
					cerr << "parse_elements (2): Bad memory allocation" << endl;
					delete[] this->data;
					this->data = NULL;
					this->num_elements = 0;
					return false;
				}
				for(temp_str_index = 0; temp_str_index < (end + 1) - start; temp_str_index++)
				{
					temp_str[temp_str_index] = data[start + temp_str_index];
				}
				temp_str[temp_str_index] = '\0';
				this->data[curr_element] = atol(temp_str);
				curr_element++;
				delete[] temp_str;
			}
		}
		else
		{
			long start = idx;
			long end = idx;
			char *temp_str;
			long internal_idx;
			for(internal_idx = start; internal_idx < data_length; internal_idx++)
			{
				if(data[internal_idx] >= '1' && data[internal_idx] <= '9')
				{
					curr_element_status = ZERO;
					end = internal_idx - 1;
					idx = end;
					break;
				}
			}
			if(internal_idx == data_length)
			{
				end = internal_idx - 1;
				idx = end;
			}
			if(start > end)
			{
				this->data[curr_element] = 0;
				curr_element++;
			}
			else
			{
				long temp_str_index;

				try
				{
					temp_str = new char[(end + 2) - start];
				}
				catch(std::bad_alloc e)
				{
					cerr << "parse_elements (3): Bad memory allocation" << endl;
					delete[] this->data;
					this->data = NULL;
					this->num_elements = 0;
					return false;
				}
				for(temp_str_index = 0; temp_str_index < (end + 1) - start; temp_str_index++)
				{
					temp_str[temp_str_index] = data[start + temp_str_index] - DELTA_LETTER_TO_NUMBER;
				}
				temp_str[temp_str_index] = '\0';
				this->data[curr_element] = atol(temp_str);
				curr_element++;
				delete[] temp_str;
			}
		}
	}
	return true;
}


long togglle_vector_get_num_elements(const char *data)
{
	int num_elements = 0;
	TOGGLE_BIN curr_element_status = ZERO;
	long data_length = strlen(data);

	if(0 == data_length)
	{
		return num_elements;
	}

	num_elements++;
	for(int idx = 0; idx < data_length; idx++)
	{
		if(ZERO == curr_element_status)
		{
			if(data[idx] >= '0' && data[idx] <= '9')
			{
				continue;
			}
			else
			{
				num_elements++;
				curr_element_status = ONE;
			}
		}
		else
		{
			if(data[idx] >= 'a' && data[idx] <= 'f')
			{
				continue;
			}
			else
			{
				num_elements++;
				curr_element_status = ZERO;
			}
		}
	}

	return num_elements;
}


CG_toggle_vector CG_toggle_vector::operator&(CG_toggle_vector& vec)
{
	TOGGLE_BIN curr_status = ZERO;
	long sum_current = 0;
	CG_toggle_vector res_vector;
	TOGGLE_BIN curr_bit;
	long num_element = 0;
	long *temp_array = NULL;

	res_vector.num_elements = 0;
	res_vector.length = this->get_length() > vec.get_length() ? this->get_length() : vec.get_length();
	res_vector.num_toggles = -1;

	for(long i = 0; i < res_vector.length; i++)
	{
		curr_bit = ((*this)[i] && vec[i]) == true ? ONE : ZERO;
		
		if(curr_bit == curr_status)
		{
			sum_current++;
		}
		else
		{
			temp_array = (long*)realloc(temp_array, (1 + num_element) * sizeof(long));
			if(NULL == temp_array)
			{
				cerr << "operator& : Bad memory realocation" << endl;
				return res_vector;
			}

			temp_array[num_element] = sum_current;
			num_element++;
			sum_current = 1;
			curr_status = (curr_status == ONE) ? ZERO : ONE;
		}
	}

	res_vector.num_elements = num_element;
	try
	{
		res_vector.data = new long[res_vector.num_elements];
	}
	catch(std::bad_alloc e)
	{
		cerr << "operator& : Bad memory allocation" << endl;
		free(temp_array);
		return res_vector;
	}
	for(num_element = 0; num_element < res_vector.num_elements; num_element++)
	{
		res_vector.data[num_element] = temp_array[num_element];
	}

	free(temp_array);
	return res_vector;
}


CG_toggle_vector CG_toggle_vector::operator|(CG_toggle_vector& vec)
{
	TOGGLE_BIN curr_status = ZERO;
	long sum_current = 0;
	CG_toggle_vector res_vector;
	TOGGLE_BIN curr_bit;
	long num_element = 0;
	long *temp_array = NULL;

	res_vector.num_elements = 0;
	res_vector.length = this->get_length() > vec.get_length() ? this->get_length() : vec.get_length();
	res_vector.num_toggles = -1;

	for(long i = 0; i < res_vector.length; i++)
	{
		curr_bit = ((*this)[i] || vec[i]) == true ? ONE : ZERO;
		if(curr_bit == curr_status)
		{
			sum_current++;
		}
		else
		{
			temp_array = (long*)realloc(temp_array, (1 + num_element) * sizeof(long));
			if(NULL == temp_array)
			{
				cerr << "operator| : Bad memory realocation" << endl;
				return res_vector;
			}

			temp_array[num_element] = sum_current;
			num_element++;
			sum_current = 1;
			curr_status = (curr_status == ONE) ? ZERO : ONE;
		}
	}

	res_vector.num_elements = num_element;
	try
	{
		res_vector.data = new long[res_vector.num_elements];
	}
	catch(std::bad_alloc e)
	{
		cerr << "operator| : Bad memory allocation" << endl;
		free(temp_array);
		return res_vector;
	}
	for(num_element = 0; num_element < res_vector.num_elements; num_element++)
	{
		res_vector.data[num_element] = temp_array[num_element];
	}

	free(temp_array);
	return res_vector;
}


CG_toggle_vector CG_toggle_vector::operator^(CG_toggle_vector& vec)
{
	TOGGLE_BIN curr_status = ZERO;
	long sum_current = 0;
	CG_toggle_vector res_vector;
	TOGGLE_BIN curr_bit;
	long num_element = 0;
	long *temp_array = NULL;

	res_vector.num_elements = 0;
	res_vector.length = this->get_length() > vec.get_length() ? this->get_length() : vec.get_length();
	res_vector.num_toggles = -1;

	for(long i = 0; i < res_vector.length; i++)
	{
		curr_bit = ((*this)[i] + vec[i]) == ONE ? ONE : ZERO;
		if(curr_bit == curr_status)
		{
			sum_current++;
		}
		else
		{
			temp_array = (long*)realloc(temp_array, (1 + num_element) * sizeof(long));
			if(NULL == temp_array)
			{
				cerr << "operator^ : Bad memory realocation" << endl;
				return res_vector;
			}

			temp_array[num_element] = sum_current;
			num_element++;
			sum_current = 1;
			curr_status = (curr_status == ONE) ? ZERO : ONE;
		}
	}

	res_vector.num_elements = num_element;
	try
	{
		res_vector.data = new long[res_vector.num_elements];
	}
	catch(std::bad_alloc e)
	{
		cerr << "operator^ : Bad memory allocation" << endl;
		free(temp_array);
		return res_vector;
	}
	for(num_element = 0; num_element < res_vector.num_elements; num_element++)
	{
		res_vector.data[num_element] = temp_array[num_element];
	}

	free(temp_array);
	return res_vector;
}